Mini-Project #03: Visualizing and Maintaining the Green Canopy of NYC
TASK 1: DOWNLOAD NYC CITY COUNCIL DISTRICT BOUNDARIES
We’ll begin by downloading data from the NYC Department of Planning site. It contains the boundaries of the 51 Council Districts that make up NYC, along with its geometry, allowing us to plot them and create maps. The following is a function used to respectfully retrieve the data from the NYC Open Data.
Show the code
library(httr)library(jsonlite)library(sf)library(dplyr)library(sf)library(kableExtra)#CREATES THE FUNCTION download_boundaries TO ACQUIRE DATA, DOES NOT TAKE PARAMETERSdownload_boundaries <-function(){#CREATES FOLDER mp03 WITHIN data, IF NEEDEDif(!dir.exists(file.path("data", "mp03"))){dir.create(file.path("data", "mp03"), showWarnings=FALSE, recursive=TRUE) }#DOWNLOADS ZIP FILE, IF NEEDED url ="https://s-media.nyc.gov/agencies/dcp/assets/files/zip/data-tools/bytes/city-council/nycc_25c.zip" destfile <-"data/mp03/nycc_25.zip"if(!file.exists(destfile)){download.file(url, destfile = destfile, mode="wb") }#UNZIPS FILE, IF NEEDED unzipped_dir <-"data/mp03/nycc_25"if (!dir.exists(unzipped_dir)) {unzip(destfile, exdir = unzipped_dir) }#READ IN SHP FILE nyc_boundaries <-st_read("data/mp03/nycc_25/nycc_25c")#TRANSFORM shp file TO WGS 84 nyc_boundaries_wgs84 <-st_transform(nyc_boundaries, crs ="WGS84")}nyc <-download_boundaries()#CHANGING INTO DATAFRAMES FOR SHORTER RENDERING TIMEnyc_df <-as.data.frame(nyc)
TASK 2: DOWNLOAD TREE POINTS
Focusing on NYC’s green spaces, we will need tree points data sourced from NYC Forestry Tree Points from NYC OpenData. The following function utilizes a while loop to respectfulling and efficiently retrieve all 1+ million observations are recorded.
Show the code
download_trees <-function() {# API URL api_url <-"https://data.cityofnewyork.us/resource/hn5i-inap.json"# PARAMETERS limit <-10000 offset <-0 all_data <-list()# Get total count of rows count_url <-paste0(api_url, "?$select=count(*)") count_response <-GET(count_url) count_text <-content(count_response, "text", encoding ="UTF-8") total_rows <-as.numeric(fromJSON(count_text)$count)message("Total rows to fetch: ", total_rows)#COLUMNS TO KEEP keep_cols <-c("objectid", "dbh", "tpstructure", "tpcondition", "stumpdiameter","plantingspaceglobalid", "geometry", "globalid", "genusspecies","createddate", "updateddate", "planteddate","riskrating", "riskratingdate", "location" )#LOOPS THROUGH TO OBTAIL ALL OBSERVATIONSwhile (offset < total_rows) { query_url <-paste0(api_url, "?$limit=", limit, "&$offset=", offset)message("Downloading offset ", offset, " ...") response <-GET(query_url) txt <-content(response, "text", encoding ="UTF-8")#PARSING page_data <-tryCatch(fromJSON(txt, flatten =TRUE), error =function(e) NULL)if (is.null(page_data) ||nrow(page_data) ==0) break#KEEPING SPECIFIED COLUMNS page_data <- page_data[, intersect(names(page_data), keep_cols), drop =FALSE]#APPEND all_data[[length(all_data) +1]] <- page_data#GIVE API A QUICK REST INBETWEEN offset <- offset + limitSys.sleep(0.3) options(scipen=999) }# COMBINE tree_data <-bind_rows(all_data)# SAVE TO CSVdir.create("data/mp03", recursive =TRUE, showWarnings =FALSE)write.csv(tree_data, "data/mp03/nyc_trees_clean.csv", row.names =FALSE)return(tree_data)}tree <-download_trees()#OVERVIEW OF tree_data#ISSUE: geometry type is characters instead of geompointssummary(tree)#CREATE tree_sf TO HAVE GEOMETRY POINTStree_sf <- tree %>%mutate(geometry =st_as_sfc(geometry, crs =4326)) %>%st_as_sf()#OVERVIEW OF tree_sf, GOOD TO GO FOR MAPPINGsummary(tree_sf)#TURNS tree_sf INTO DATAFRAME FOR FASTER RENDERINGtree_sf_df <-as.data.frame(tree_sf)
TASK 3: PLOT ALL TREE POINTS
Both datasets contain geographic coordinates that can be visualized on a map. Plotting the Council District boundaries will give us the base. By overlaying the tree point data, we can observe the distribution of greenery throughout NYC.
Show the code
library(ggplot2)ggplot() +geom_sf(data = nyc, mapping=aes(), color ="black") +#NYC COUNCIL DISTRICT MAPgeom_sf(data = tree_sf_df$geometry, color ="darkgreen", mapping=aes(), size =0.05, alpha =0.2) +#TREE POINTSscale_fill_viridis_c() +theme_minimal() +labs(title ="NYC Tree Points within Council District Boundaries",caption ="Source: NYC OpenData" )
ZOOM IN FOR A CLOSER LOOK
Show the code
library(leaflet)leaflet() |>addTiles() |>addPolygons(data = nyc, color ="black", fill =FALSE) |>addCircleMarkers(data = tree_sf, radius =2, color ="forestgreen", stroke =FALSE, fillOpacity =0.5)